package com.ternnetwork.oa.service.impl.settings;

import com.ternnetwork.commons.model.http.BaseResponse;
import com.ternnetwork.core.accesscontroll.entity.User;
import com.ternnetwork.core.accesscontroll.service.UserService;
import com.ternnetwork.core.accesscontroll.vo.SimpleUser;

import com.ternnetwork.oa.dto.settings.UserRelationshipDto;
import com.ternnetwork.oa.dto.settings.UserRelationshipQueryDto;

import com.ternnetwork.oa.entity.settings.UserRelationship;
import com.ternnetwork.oa.entity.settings.UserRelationshipId;
import com.ternnetwork.oa.enums.settings.UserRelationshipType;
import com.ternnetwork.oa.repository.settings.UserRelationshipRepository;
import com.ternnetwork.oa.service.settings.UserRelationshipService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;


@Service
public class UserRelationshipServiceImpl implements UserRelationshipService {

    @Autowired
    private UserService userService;

    @Autowired
    private UserRelationshipRepository userRelationshipRepository;

    @Autowired
    private EntityManager entityManager;

    @Override
    public BaseResponse findAll(String username, String type) {

        User loginUser=userService.findByName(username);

        UserRelationshipType userRelationshipType=UserRelationshipType.valueOf(type);

        List<UserRelationship> userRelationshipList=userRelationshipRepository.findAll(loginUser.getId(),userRelationshipType);

        List<SimpleUser> simpleUserList=new ArrayList<>(10);

        for(UserRelationship userRelationship:userRelationshipList){

            SimpleUser simpleUser=new SimpleUser();

            BeanUtils.copyProperties(userRelationship.getUserTo(),simpleUser);

            simpleUserList.add(simpleUser);
        }

        BaseResponse baseResponse=new BaseResponse();

        baseResponse.setSccucess(simpleUserList);

        return baseResponse;
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor=Exception.class)
    @Override
    public BaseResponse save(String username, UserRelationshipDto userRelationshipDto) {

        User userFrom=userService.findByName(username);

        UserRelationshipType userRelationshipType=UserRelationshipType.valueOf(userRelationshipDto.getType());

        List<Long> userToIdList=userRelationshipDto.getUserIds();

        if(userToIdList.size()==0){
            userRelationshipRepository.deleteAllByUserFromId(userRelationshipType,userFrom.getId());
        }else{
            userRelationshipRepository.deleteAllByUserFromIdAndExcludeUserToIdList(userRelationshipType,userFrom.getId(),userToIdList);
        }

        for(long userToId:userToIdList){

            User userTo=(User)userService.getOne(userToId).getData();

            saveUserRelationship(userFrom,userTo,userRelationshipType);

        }

        BaseResponse baseResponse=new BaseResponse();

        baseResponse.setSccucess();

        return baseResponse;
    }

    private void saveUserRelationship(User userFrom, User userTo,UserRelationshipType userRelationshipType) {

        UserRelationshipId id =new UserRelationshipId(userFrom.getId(),userTo.getId(),userRelationshipType);;

        Optional<UserRelationship> old= userRelationshipRepository.findById(id);

        if(old.equals(Optional.empty())){

            UserRelationship t=new UserRelationship();

            t.setId(id);

            t.setUserFrom(userFrom);

            t.setUserTo(userTo);

            t.setCreateTime(new Date());

            t.setCreatedById(userFrom.getId());

            userRelationshipRepository.saveAndFlush(t);

        }

    }



    @Override
    public BaseResponse findByPage(Pageable pageable, UserRelationshipQueryDto dto, String username) {

        BaseResponse  baseResponse=new BaseResponse();

        User loginUser=userService.findByName(username);

        dto.setUserToId(loginUser.getId());

        CriteriaBuilder criteriaBuilder =this.entityManager.getCriteriaBuilder();

        CriteriaQuery<UserRelationship> criteriaQuery=criteriaBuilder.createQuery(UserRelationship.class);

        Root<UserRelationship> root=criteriaQuery.from(UserRelationship.class);

        //tell it what field we need here
        criteriaQuery.multiselect(
                        root.get("userFrom"))
                .where(getPredicate(dto,root,criteriaBuilder));

        List<UserRelationship> noteList=this.entityManager.createQuery(criteriaQuery)
                .setFirstResult((int)pageable.getOffset())
                .setMaxResults(pageable.getPageSize()).getResultList();

        Page<UserRelationship> page= PageableExecutionUtils.getPage(noteList,pageable,()->this.userRelationshipRepository.count(getSpecification(dto)));

        baseResponse.setSccucess(page);

        return baseResponse;
    }

    private Specification getSpecification(UserRelationshipQueryDto dto){

        return (root, criteriaQuery, criteriaBuilder) -> {

            return criteriaQuery.where(getPredicate(dto,root,criteriaBuilder)).getRestriction();
        };

    }

    private Predicate[]  getPredicate(UserRelationshipQueryDto dto, Root<UserRelationship> root, CriteriaBuilder criteriaBuilder){

        List<Predicate> predicates = new ArrayList<>();

        if(!StringUtils.isEmpty(dto.getType())){
            predicates.add(criteriaBuilder.equal(root.get("type").as(UserRelationshipType.class),UserRelationshipType.valueOf(dto.getType())));
        }
        if(!StringUtils.isEmpty(dto.getName())){

            predicates.add(criteriaBuilder.like(root.get("userFrom").get("name").as(String.class),"%".concat(dto.getName()).concat("%")));

        }
        if(!StringUtils.isEmpty(dto.getRealName())){

            predicates.add(criteriaBuilder.like(root.get("userFrom").get("realName").as(String.class),"%".concat(dto.getRealName()).concat("%")));

        }
        if(!StringUtils.isEmpty(dto.getDepartmentName())){

            predicates.add(criteriaBuilder.like(root.get("userFrom").get("department").get("nameZh").as(String.class),"%".concat(dto.getDepartmentName().toLowerCase()).concat("%")));

        }

        if(!StringUtils.isEmpty(dto.getEmail())){
            predicates.add(criteriaBuilder.equal(root.get("userFrom").get("email").as(String.class), dto.getEmail()));
        }

        predicates.add(criteriaBuilder.equal(root.get("userFrom").get("deleted").as(Boolean.class),false));

        predicates.add(criteriaBuilder.equal(root.get("userTo").get("id").as(Long.class),dto.getUserToId()));

        Predicate[] predicateArray = new Predicate[predicates.size()];

        return predicates.toArray(predicateArray);
    }

}
